fix: don't bound source-target distance#431
Conversation
🤖 Kimi Code ReviewCritical Security Issue: Unbounded computation DoS vector
The removal of
Question: What is the rationale for removing this protection? If it's to support long-range sync or historical attestation processing, consider making the limit configurable or context-dependent (unlimited during sync, capped during gossip). Minor: The warning log removal reduces observability. If you keep a limit, retain the log to detect potential attacks. Automated review by Kimi (Moonshot AI) · kimi-k2.5 · custom prompt |
🤖 Claude Code ReviewNow I have enough context for the review. PR 431 Review:
|
🤖 Codex Code Review
Assumptions/testing gap: I could not run Automated review by OpenAI Codex · gpt-5.4 · custom prompt |
Greptile SummaryRemoves the
Confidence Score: 4/5Safe to merge — the fix correctly removes the cap that was truncating valid attestations; the only remaining nit is a stale doc comment. The change is small and targeted: dropping a step counter and a constant that were causing legitimate attestations to be rejected after 128 slots. The loop already terminates correctly via the slot comparison and the missing-entry guard. The only issue is the doc comment still describing the removed cap, including a dangling rustdoc link to the deleted constant. The doc comment in
|
| Filename | Overview |
|---|---|
| crates/blockchain/src/store.rs | Removes the 128-step cap in checkpoint_is_ancestor that was incorrectly bounding the finality period; the loop now terminates only by slot comparison or a missing store entry. The doc comment was not updated and still references the removed constant. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[checkpoint_is_ancestor] --> B{ancestor.slot >= descendant.slot}
B -- Yes --> C{slots equal AND roots match}
C -- Yes --> D[return true]
C -- No --> E[return false]
B -- No --> F[current_root = descendant_header.parent_root]
F --> G{get_block_header current_root}
G -- None --> E
G -- Some header --> I{header.slot == ancestor.slot}
I -- Yes --> J{current_root == ancestor.root}
J -- Yes --> D
J -- No --> E
I -- No --> K{header.slot less than ancestor.slot}
K -- Yes --> E
K -- No --> L[current_root = header.parent_root]
L --> G
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
crates/blockchain/src/store.rs:119-126
The doc comment still refers to `MAX_ANCESTRY_WALK_SLOTS` and the walk-cap behavior ("up to `[MAX_ANCESTRY_WALK_SLOTS]` steps", "exceeding the walk cap, yields `false`"), both of which were removed by this PR. The dangling link will also produce a `rustdoc` warning (`[MAX_ANCESTRY_WALK_SLOTS]` resolves to nothing).
```suggestion
/// Return whether `ancestor` lies on `descendant`'s parent chain.
///
/// `descendant_header` is the descendant's already-fetched header, so the walk
/// starts from its parent without re-reading it. Walks parent links down to the
/// ancestor's slot. Empty (skipped) slots on the path are traversed
/// transparently since they carry no block.
/// Conservative: a block missing from the store yields `false`.
```
Reviews (1): Last reviewed commit: "docs: update function documentation" | Re-trigger Greptile
| /// Return whether `ancestor` lies on `descendant`'s parent chain. | ||
| /// | ||
| /// `descendant_header` is the descendant's already-fetched header, so the walk | ||
| /// starts from its parent without re-reading it. Walks parent links down to the | ||
| /// ancestor's slot, up to [`MAX_ANCESTRY_WALK_SLOTS`] steps. Empty (skipped) | ||
| /// slots on the path are traversed transparently since they carry no block. | ||
| /// Conservative: a block missing from the store, or exceeding the walk cap, | ||
| /// yields `false`. | ||
| /// ancestor's slot. Empty (skipped) slots on the path are traversed transparently | ||
| /// since they carry no block. A block missing from the store yields `false`. | ||
| fn checkpoint_is_ancestor( | ||
| store: &Store, |
There was a problem hiding this comment.
The doc comment still refers to
MAX_ANCESTRY_WALK_SLOTS and the walk-cap behavior ("up to [MAX_ANCESTRY_WALK_SLOTS] steps", "exceeding the walk cap, yields false"), both of which were removed by this PR. The dangling link will also produce a rustdoc warning ([MAX_ANCESTRY_WALK_SLOTS] resolves to nothing).
| /// Return whether `ancestor` lies on `descendant`'s parent chain. | |
| /// | |
| /// `descendant_header` is the descendant's already-fetched header, so the walk | |
| /// starts from its parent without re-reading it. Walks parent links down to the | |
| /// ancestor's slot, up to [`MAX_ANCESTRY_WALK_SLOTS`] steps. Empty (skipped) | |
| /// slots on the path are traversed transparently since they carry no block. | |
| /// Conservative: a block missing from the store, or exceeding the walk cap, | |
| /// yields `false`. | |
| /// ancestor's slot. Empty (skipped) slots on the path are traversed transparently | |
| /// since they carry no block. A block missing from the store yields `false`. | |
| fn checkpoint_is_ancestor( | |
| store: &Store, | |
| /// Return whether `ancestor` lies on `descendant`'s parent chain. | |
| /// | |
| /// `descendant_header` is the descendant's already-fetched header, so the walk | |
| /// starts from its parent without re-reading it. Walks parent links down to the | |
| /// ancestor's slot. Empty (skipped) slots on the path are traversed | |
| /// transparently since they carry no block. | |
| /// Conservative: a block missing from the store yields `false`. |
Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/blockchain/src/store.rs
Line: 119-126
Comment:
The doc comment still refers to `MAX_ANCESTRY_WALK_SLOTS` and the walk-cap behavior ("up to `[MAX_ANCESTRY_WALK_SLOTS]` steps", "exceeding the walk cap, yields `false`"), both of which were removed by this PR. The dangling link will also produce a `rustdoc` warning (`[MAX_ANCESTRY_WALK_SLOTS]` resolves to nothing).
```suggestion
/// Return whether `ancestor` lies on `descendant`'s parent chain.
///
/// `descendant_header` is the descendant's already-fetched header, so the walk
/// starts from its parent without re-reading it. Walks parent links down to the
/// ancestor's slot. Empty (skipped) slots on the path are traversed
/// transparently since they carry no block.
/// Conservative: a block missing from the store yields `false`.
```
How can I resolve this? If you propose a fix, please make it concise.
🗒️ Description / Motivation
This PR fixes an issue we found during some devnet runs, that a step-limit we added to bound the ancestor-check loop inadvertently also capped the finality period to 128 slots before the devnet stopped including attestations.